package com.zm.zmgz.configure;

import com.zm.zmgz.annotationzm.ButtonAuthorityAnnotation;
import com.zm.zmgz.pojo.AuthorityBean;
import com.zm.zmgz.pojo.Response;
import com.zm.zmgz.pojo.UserBean;
import com.zm.zmgz.service.AuthorityService;
import org.aspectj.lang.JoinPoint;
        import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Map;

/**
 * AOP切面
 *
 * 以下几个增强的执行顺序是:
 *     1.aroundAdvice(.proceed();语句之前的代码)
 *     2.beforeAdvice
 *     3.被增强方法
 *     4.aroundAdvice(.proceed();语句之后的代码)
 *     5.afterAdvice
 *     6.afterReturningAdvice
 *     7.afterThrowingAdvice（有异常时才会走，无异常不会走此方法）
 *
 *  注: 当被增强方法 或 afterAdvice 或 afterReturningAdvice抛出异常时，会被afterThrowingAdvice
 *      捕获到异常，进而短路走 afterThrowingAdvice方法
 *
 * @Auther: cjw
 * @Date: 2020/03/22/21:46
 * @Description:
 */
@Aspect
@Configuration
public class AopConfig {

    @Autowired
    AuthorityService authorityService;

    Boolean sign = true; // 权限标记,为false,设置status状态码为40300,把data属性置为null

    /**
     * 将表达式【* com.aspire.controller.AopController.*(..))】所匹配的所有方法标记为切点，
     * 切点名为 executeAdvice()
     *
     * 注:execution里的表达式所涉及到的类名(除了基本类以外)，其它的要用全类名;干脆不管是不
     *    是基础类，都推荐使用全类名
     * 注:如果有多个表达式进行交集或者并集的话，可以使用&&、||、or，示例:
     * @Pointcut(
     *  "execution(public com.szlzcl.laodeduo.common.CommonResponse com.szlzcl.laodeduo.*.controller..*(..)) "
     *      + " || "
     *          + "execution(public com.szlzcl.laodeduo.common.CommonResponse com.szlzcl.laodeduo.config.common..*(..))"
     *  )
     *
     * @author JustryDeng
     * @date 2018/12/18 13:43
     */
//    @Pointcut("execution(* com.aspire.controller.AopController.*(..))")
    /**
     * 使用注解来定位AOP作为节点的方法们
     */
    @Pointcut("@annotation(com.zm.zmgz.annotationzm.ButtonAuthorityAnnotation)")
    public void executeAdvice() {
    }

    public Boolean getSign() {
        return sign;
    }

    public void setSign(Boolean sign) {
        this.sign = sign;
    }

    /**
     * 切点executeAdvice()的前置增强方法
     *
     * @author JustryDeng
     * @date 2018/12/18 13:47
     */
    @Before(value = "executeAdvice()")
    public void beforeAdvice(JoinPoint joinPoint) {
        Object[] paramArray = joinPoint.getArgs(); // 获取参数数组
        String threadName = Thread.currentThread().getName(); // 获取线程名
        System.out.println(threadName + " -> beforeAdvice获取到了被增强方法的参数了，为:"
                + Arrays.toString(paramArray));
    }

    /**
     * 切点executeAdvice()的后增强方法
     *
     * @author JustryDeng
     * @date 2018/12/18 13:47
     */
    @After("executeAdvice()")
    public void afterAdvice() {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " -> 后置增强afterAdvice执行了");
    }

    /**
     * 切点executeAdvice()的后增强方法
     *
     * 注:当被增强方法 或  afterAdvice正常执行时，才会走此方法
     * 注: returning指明获取到的(环绕增强返回的)返回值
     *
     * @author JustryDeng
     * @date 2018/12/18 13:47
     */
    @AfterReturning(value = "executeAdvice()", returning = "map")
    public void afterReturningAdvice(Map<String, Object> map) {
        String threadName = Thread.currentThread().getName();
        System.out.println(threadName + " -> afterReturningAdvice获得了返回结果 map -> " + map);

    }

    /**
     *  当被增强方法 或 afterAdvice 或 afterReturningAdvice抛出异常时，会被afterThrowingAdvice
     *  捕获到异常，进而短路走 afterThrowingAdvice方法
     *
     * @author JustryDeng
     * @date 2018/12/18 13:57
     */
    @AfterThrowing(value = "executeAdvice()", throwing ="ex")
    public void afterThrowingAdvice(Exception ex) {
        System.out.println("AfterThrowing捕获到了 --->" + ex);
    }

    /**
     *  环绕增强 会在 被增强方法执行完毕后  第一个执行，
     *  所以在绝大多数时候，我们都直接返回thisJoinPoint.proceed();的返回值；
     *  如果此方法返回null,那么@AfterReturning方法获取到的返回值 就会是null
     *
     * @throws Throwable 当目标方法抛出异常时
     *
     * @author JustryDeng
     * @date 2018/12/18 13:57
     */
    @Around("executeAdvice()")
    public Object aroundAdvice(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕方法开始执行!");
        String name = joinPoint.getSignature().getName(); // 被增强方法的方法名
        System.out.println("需要进行权限验证的方法名:"+name);
        Object[] paramArray = joinPoint.getArgs(); // 获取参数数组
        String identificationnumber = (String)paramArray[0]; // token(identificationnumber)参数都放在第一个,方便获取. // 身份证
        System.out.println("需要进行权限验证的身份证:"+identificationnumber);
        String declaringTypeName = joinPoint.getSignature().getDeclaringTypeName(); // 被增强方法的类名全路径
        System.out.println("被代理方法的类的全名:"+declaringTypeName);
        Class<?> aClass = null;
        String value = null; // 权限路径
        try {
            aClass = Class.forName(declaringTypeName);
            Method[] declaredMethods = aClass.getDeclaredMethods();
            Method declaredMethod = null;  // 被增强方法对象
            for (Method method : declaredMethods) {
                method.setAccessible(true);
                if(method.getName() == name){
                    System.out.println("找到了被拦截的方法!");
                    declaredMethod=method;
                    break;
                }
            }
            declaredMethod.setAccessible(true);
            ButtonAuthorityAnnotation annotation = declaredMethod.getAnnotation(ButtonAuthorityAnnotation.class);
            value = annotation.value();
            System.out.println("需要验证的权限:"+value);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        List<AuthorityBean> buttonAuthority = authorityService.getAuthority(identificationnumber, value);
        if(buttonAuthority.size()<1) {
            System.out.println("查询结果:权限不足!");
            System.out.println("环绕方法执行结束!");
            return new Response(40300,"权限不足!",null);
        }else {
            System.out.println("查询结果:拥有权限!");
            System.out.println("环绕方法执行结束!");
            return joinPoint.proceed();  // 执行被增强方法，并获取到返回值 类似于 过滤器的chain.doFilter(req,resp)方法
        }
    }
}